package com.googlecode.mapperdao

import org.scalatest.{Matchers, FunSuite}
import com.googlecode.mapperdao.jdbc.Setup
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner

/**
 * @author kostantinos.kougios
 *
 *         26 Jan 2012
 */
@RunWith(classOf[JUnitRunner])
class OneToManyExternalEntitySuite extends FunSuite with Matchers
{

	val (jdbc, mapperDao, queryDao) = Setup.setupMapperDao(List(HouseEntity, PersonEntity))

	if (Setup.database == "h2") {
		test("persist/select") {
			createTables()

			val person = Person("p1", HouseEntity.currentData.toSet)
			val inserted = mapperDao.insert(PersonEntity, person)
			inserted should be === person
			HouseEntity.addedStore should be(HouseEntity.currentData)

			mapperDao.select(PersonEntity, inserted.id).get should be === inserted
		}

		test("update, remove") {
			createTables()

			val person = Person("p1", HouseEntity.currentData.toSet)
			val inserted = mapperDao.insert(PersonEntity, person)
			val toUpdate = Person("p1-1", inserted.owns.filter(_.id == 11))
			val updated = mapperDao.update(PersonEntity, inserted, toUpdate)
			HouseEntity.removedStore should be(List(House(10, "House10")))
			HouseEntity.addedStore should be(Nil)
			HouseEntity.intersectionStore should be((House(11, "House11"), House(11, "House11")) :: Nil)
			updated should be === toUpdate
			mapperDao.select(PersonEntity, inserted.id).get should be === updated
		}

		test("update, add") {
			createTables()

			val person = Person("p1", Set(House(10, "H10")))
			val inserted = mapperDao.insert(PersonEntity, person)
			val toUpdate = Person("p1-1", inserted.owns + House(11, "H11"))
			val updated = mapperDao.update(PersonEntity, inserted, toUpdate)
			HouseEntity.addedStore should be(List(House(11, "H11")))
			HouseEntity.removedStore should be(Nil)
			HouseEntity.intersectionStore should be(List((House(10, "H10"), House(10, "H10"))))
			updated should be === toUpdate
			mapperDao.select(PersonEntity, inserted.id).get should be === updated
		}

		test("update, intersect") {
			createTables()

			val person = Person("p1", Set(House(10, "H10")))
			val inserted = mapperDao.insert(PersonEntity, person)
			inserted.owns.head.address = "updated"
			val toUpdate = Person("p1-1", inserted.owns)
			val updated = mapperDao.update(PersonEntity, inserted, toUpdate)
			HouseEntity.addedStore should be(Nil)
			HouseEntity.removedStore should be(Nil)
			HouseEntity.intersectionStore should be(List((House(10, "updated"), House(10, "updated"))))
			updated should be === toUpdate
			mapperDao.select(PersonEntity, inserted.id).get should be === updated
		}

		test("delete without propagation") {
			createTables()

			val person = Person("p1", Set(House(11, "house for 1"), House(12, "2nd house for 1")))
			val inserted = mapperDao.insert(PersonEntity, person)
			mapperDao.delete(PersonEntity, inserted)
			HouseEntity.onDeleteCount should be === 0
			mapperDao.select(PersonEntity, inserted.id) should be(None)
		}
		test("delete with propagation") {
			createTables()

			val person = Person("p1", Set(House(11, "house for 1"), House(12, "2nd house for 1")))
			val inserted = mapperDao.insert(PersonEntity, person)
			mapperDao.delete(DeleteConfig(propagate = true), PersonEntity, inserted)
			HouseEntity.onDeleteCount should be === 1
			mapperDao.select(PersonEntity, inserted.id) should be(None)
		}
	}

	def createTables() {
		HouseEntity.reset()
		Setup.dropAllTables(jdbc)
		Setup.queries(this, jdbc).update("ddl")
	}

	case class Person(var name: String, owns: Set[House])

	case class House(id: Int, var address: String)

	object PersonEntity extends Entity[Int, SurrogateIntId, Person]
	{
		val id = key("id") autogenerated (_.id)
		val name = column("name") to (_.name)
		val owns = onetomany(HouseEntity) to (_.owns)

		def constructor(implicit m: ValuesMap) = new Person(name, owns) with Stored
		{
			val id: Int = PersonEntity.id
		}
	}

	object HouseEntity extends ExternalEntity[Int, House]
	{

		var currentData: List[House] = Nil
		var addedStore: List[House] = Nil
		var intersectionStore: List[(House, House)] = Nil
		var removedStore: List[House] = Nil

		def reset() {
			addedStore = Nil
			intersectionStore = Nil
			removedStore = Nil
			currentData = List(House(10, "House10"), House(11, "House11"))
		}

		onSelectOneToMany(PersonEntity.owns) {
			_.foreignIds match {
				case List(foreignId: Int) => currentData
				case _ => throw new RuntimeException
			}
		}
		onUpdateOneToMany(PersonEntity.owns) {
			case UpdateExternalOneToMany(updateConfig, newVM, added, intersection, removed) =>
				this.addedStore = added.toList
				this.intersectionStore = intersection.toList
				this.removedStore = removed.toList
				currentData = (added ++ intersection.map(_._2)).toList
		}

		var onDeleteCount = 0
		onDeleteOneToMany(PersonEntity.owns) {
			d =>
				onDeleteCount += 1
		}
	}

}